﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Media.Media3D;
using System.Windows.Media.Animation;
using _3DTools;
using System.ComponentModel;
using System.Collections.ObjectModel;

namespace _3DView
{
    public enum RotateDirection : int
    {
        None = -1,
        Left = 0,
        Right = 1
    }

    /// <summary>
    /// Interaction logic for _3DViewControl.xaml
    /// </summary>
    public partial class _3DViewControl : UserControl
    {
        public _3DViewControl()
        {
            InitializeComponent();
            this.Children = new ObservableCollection<_3DViewElement>();
            //Init();
        }

        /// <summary>
        /// BackElement transf
        /// </summary>
        TransformGroup __BackElementRotation;


        /// <summary>
        /// Render transform for all content
        /// </summary>
        RotateTransform __ElementRotation;

        #region Proprietes
        /// <summary>
        /// Gets or sets the current item
        /// </summary>
        public int CurrentItem
        { get; set; }

        /// <summary>
        /// Gets or sets the current item offset on Z axis
        /// </summary>
        public int CurrentItemOffset { get; set; }

        /// <summary>
        /// Gets or sets the miliseconds in the animation duration
        /// </summary>
        public int TimeSpanMiliseconds { get; set; }

        /// <summary>
        /// Gets a collection of child _3DViewElement objects.
        /// </summary>        
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public ObservableCollection<_3DViewElement> Children { get; set; }
        #endregion

        #region Methods

        /// <summary>
        /// Removes the element located at an index
        /// </summary>
        /// <param name="index">Element index to remove</param>
        public void RemoveAt(int index)
        {
            if (index < 0 || index >= this.MyInteractiveVisual3D.Children.Count)
                return;

            if (index > this.CurrentItem)
            {
                this.TranslateRightItems(RotateDirection.Left, index);
            }
            else
                if (index < CurrentItem)
                {
                    this.TranslateLeftItems(RotateDirection.Right, index + 1);
                    --this.CurrentItem;
                }
                else
                {
                    InteractiveVisual3D currentChild = this.MyInteractiveVisual3D.Children[index] as InteractiveVisual3D;
                    if (currentChild == null) return;

                    Transform3DGroup currentTransf = currentChild.Transform as Transform3DGroup;
                    TranslateTransform3D currentTranslate = (currentTransf.Children[0] as TranslateTransform3D);
                    RotateTransform3D currentRotation = (currentTransf.Children[1] as RotateTransform3D);

                    DoubleAnimation myCurrentTranslateXDoubleAnimation = null,
                                    myCurrentTranslateZDoubleAnimation = null,
                                    myCurrentRotateDoubleAnimation = null;

                    //check for left child
                    if (index - 1 >= 0 && this.MyInteractiveVisual3D.Children[index - 1] != null)
                    {
                        currentChild = this.MyInteractiveVisual3D.Children[index - 1] as InteractiveVisual3D;
                        if (currentChild == null) return;

                        currentTransf = currentChild.Transform as Transform3DGroup;
                        currentTranslate = (currentTransf.Children[0] as TranslateTransform3D);
                        currentRotation = (currentTransf.Children[1] as RotateTransform3D);

                        // Animate the rotation's angle.
                        currentRotation.CenterX = -1; currentRotation.CenterZ = 0;
                        myCurrentRotateDoubleAnimation = new DoubleAnimation();
                        myCurrentRotateDoubleAnimation.From = 45;
                        myCurrentRotateDoubleAnimation.To = 0;
                        myCurrentRotateDoubleAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(TimeSpanMiliseconds / 2));
                        myCurrentRotateDoubleAnimation.AutoReverse = false;
                        myCurrentRotateDoubleAnimation.RepeatBehavior = new RepeatBehavior(1d);

                        // Apply the animation to the rotation's angle property.
                        currentRotation.Rotation.BeginAnimation(AxisAngleRotation3D.AngleProperty, myCurrentRotateDoubleAnimation);

                        // Animate the Translate's OffsetX.
                        myCurrentTranslateXDoubleAnimation = new DoubleAnimation();
                        myCurrentTranslateXDoubleAnimation.From = -1;
                        myCurrentTranslateXDoubleAnimation.To = 0;
                        myCurrentTranslateXDoubleAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(TimeSpanMiliseconds / 2));
                        myCurrentTranslateXDoubleAnimation.AutoReverse = false;
                        myCurrentTranslateXDoubleAnimation.RepeatBehavior = new RepeatBehavior(1d);

                        // Apply the animation to the Translate's OffsetX property.
                        currentTranslate.BeginAnimation(TranslateTransform3D.OffsetXProperty, myCurrentTranslateXDoubleAnimation);

                        // Animate the Translate's OffsetZ.
                        myCurrentTranslateZDoubleAnimation = new DoubleAnimation();
                        myCurrentTranslateZDoubleAnimation.From = 0;
                        myCurrentTranslateZDoubleAnimation.To = this.CurrentItemOffset;
                        myCurrentTranslateZDoubleAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(TimeSpanMiliseconds / 2));
                        myCurrentTranslateZDoubleAnimation.AutoReverse = false;
                        myCurrentTranslateZDoubleAnimation.RepeatBehavior = new RepeatBehavior(1d);

                        // Apply the animation to the Translate's OffsetX property.
                        currentTranslate.BeginAnimation(TranslateTransform3D.OffsetZProperty, myCurrentTranslateZDoubleAnimation);

                        //translate all left items
                        this.TranslateLeftItems(RotateDirection.Right, index);

                        this.CurrentItem = index - 1;
                    }
                    else
                        if (index + 1 < this.MyInteractiveVisual3D.Children.Count && this.MyInteractiveVisual3D.Children[index + 1] != null)
                        {
                            currentChild = this.MyInteractiveVisual3D.Children[index + 1] as InteractiveVisual3D;
                            if (currentChild == null) return;

                            Transform3DGroup currentTransf1 = currentChild.Transform as Transform3DGroup;
                            TranslateTransform3D currentTranslate1 = (currentTransf1.Children[0] as TranslateTransform3D);
                            RotateTransform3D currentRotation1 = (currentTransf1.Children[1] as RotateTransform3D);

                            // Animate the rotation's angle.
                            currentRotation1.CenterX = 1; currentRotation1.CenterZ = 0;
                            myCurrentRotateDoubleAnimation = new DoubleAnimation();
                            myCurrentRotateDoubleAnimation.From = -45;
                            myCurrentRotateDoubleAnimation.To = 0;
                            myCurrentRotateDoubleAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(TimeSpanMiliseconds / 2));
                            myCurrentRotateDoubleAnimation.AutoReverse = false;
                            myCurrentRotateDoubleAnimation.RepeatBehavior = new RepeatBehavior(1d);
                            // Apply the animation to the rotation's angle property.
                            currentRotation1.Rotation.BeginAnimation(AxisAngleRotation3D.AngleProperty, myCurrentRotateDoubleAnimation);

                            //currentTranslate = new TranslateTransform3D(-constVal, 0, this.CurrentItemOffset - constVal);
                            // Animate the Translate's OffsetX.
                            myCurrentTranslateXDoubleAnimation = new DoubleAnimation();
                            myCurrentTranslateXDoubleAnimation.From = 1;
                            myCurrentTranslateXDoubleAnimation.To = 0;
                            myCurrentTranslateXDoubleAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(TimeSpanMiliseconds / 2));
                            myCurrentTranslateXDoubleAnimation.AutoReverse = false;
                            myCurrentTranslateXDoubleAnimation.RepeatBehavior = new RepeatBehavior(1d);
                            // Apply the animation to the Translate's OffsetX property.
                            currentTranslate1.BeginAnimation(TranslateTransform3D.OffsetXProperty, myCurrentTranslateXDoubleAnimation);

                            //Animate the Translate's OffsetZ.
                            myCurrentTranslateZDoubleAnimation = new DoubleAnimation();
                            myCurrentTranslateZDoubleAnimation.From = 0;
                            myCurrentTranslateZDoubleAnimation.To = this.CurrentItemOffset;
                            myCurrentTranslateZDoubleAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(TimeSpanMiliseconds / 2));
                            myCurrentTranslateZDoubleAnimation.AutoReverse = false;
                            myCurrentTranslateZDoubleAnimation.RepeatBehavior = new RepeatBehavior(1d);

                            //Apply the animation to the Translate's OffsetX property.
                            currentTranslate1.BeginAnimation(TranslateTransform3D.OffsetZProperty, myCurrentTranslateZDoubleAnimation);

                            this.TranslateRightItems(RotateDirection.Left, index + 1);

                            this.CurrentItem = index + 1;
                        }
                }

            this.MyInteractiveVisual3D.Children.RemoveAt(index);
            this.Children.RemoveAt(index);
        }

        /// <summary>
        /// Adds a new child
        /// </summary>
        /// <param name="child">Child to add</param>
        public void AddNewChild(_3DView._3DViewElement child)
        {
            this.Children.Add(child);

            //the content element
            StackPanel panel = new StackPanel();

            //reflection
            Infragistics.ToyBox.Reflector r = new Infragistics.ToyBox.Reflector();
            r.ReflectionTarget = child.FrontElement;

            //add to content
            panel.Children.Add(child.FrontElement);
            panel.Children.Add(r);

            //reflection
            Infragistics.ToyBox.Reflector r1 = new Infragistics.ToyBox.Reflector();
            r1.ReflectionTarget = child.BackElement;

            //add to content
            panel.Children.Add(child.BackElement); child.BackElement.Visibility = Visibility.Collapsed;
            panel.Children.Add(r1); r1.Visibility = Visibility.Collapsed;

            child.BackElement.RenderTransformOrigin = new Point(0.5, 0.5);
            r1.RenderTransformOrigin = new Point(0.5, 0.5);
            child.BackElement.RenderTransform = __BackElementRotation; r1.RenderTransform = __BackElementRotation;
            panel.RenderTransform = __ElementRotation;

            //add 2D content
            InteractiveVisual3D visualChild = new InteractiveVisual3D();
            visualChild.IsBackVisible = true;
            visualChild.Geometry = this.FindResource("ItemMesh") as MeshGeometry3D;
            visualChild.Visual = panel;

            Transform3DGroup transf = visualChild.Transform as Transform3DGroup;
            if (transf == null) transf = this.CreateTransform();

            RotateTransform3D rot = (transf.Children[1] as RotateTransform3D);
            TranslateTransform3D translate = (transf.Children[0] as TranslateTransform3D);

            transf.Children.Clear();

            translate.OffsetX = this.Children.Count - this.CurrentItem - 1;
            rot.CenterX = this.Children.Count - this.CurrentItem - 1;
            rot.Rotation.SetValue(AxisAngleRotation3D.AngleProperty, (double)-45);

            transf.Children.Add(translate); transf.Children.Add(rot);
            visualChild.Transform = transf;

            this.MyInteractiveVisual3D.Children.Add(visualChild);
        }

        /// <summary>
        /// Adds a new child
        /// </summary>
        /// <param name="child">Child to add</param>
        void AddChild(_3DView._3DViewElement child)
        {
            //the content element
            StackPanel panel = new StackPanel();

            //reflection
            Infragistics.ToyBox.Reflector r = new Infragistics.ToyBox.Reflector();
            r.ReflectionTarget = child.FrontElement;

            //add to content
            panel.Children.Add(child.FrontElement);
            panel.Children.Add(r);

            //reflection
            Infragistics.ToyBox.Reflector r1 = new Infragistics.ToyBox.Reflector();
            r1.ReflectionTarget = child.BackElement;

            //add to content
            panel.Children.Add(child.BackElement); child.BackElement.Visibility = Visibility.Collapsed;
            panel.Children.Add(r1); r1.Visibility = Visibility.Collapsed;

            child.BackElement.RenderTransformOrigin = new Point(0.5, 0.5);
            r1.RenderTransformOrigin = new Point(0.5, 0.5);
            child.BackElement.RenderTransform = __BackElementRotation; r1.RenderTransform = __BackElementRotation;

            //add 2D content
            InteractiveVisual3D visualChild = new InteractiveVisual3D();
            visualChild.IsBackVisible = true;
            visualChild.Geometry = this.FindResource("ItemMesh") as MeshGeometry3D;
            visualChild.Visual = panel;

            //add to list
            this.MyInteractiveVisual3D.Children.Add(visualChild);
        }

        /// <summary>
        /// Creates a new Transform3DGroup
        /// </summary>
        /// <returns></returns>
        Transform3DGroup CreateTransform()
        {
            Transform3DGroup transf = new Transform3DGroup();

            transf.Children.Add(new TranslateTransform3D(0, 0, 0));
            transf.Children.Add(new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 1, 0), 0)));

            return transf;
        }

        /// <summary>
        /// Makes 3D View initializations
        /// </summary>
        public void Init()
        {
            __ElementRotation = new RotateTransform(-90);

            __BackElementRotation = new TransformGroup();
            __BackElementRotation.Children.Add(new ScaleTransform(-1, 1));

            if (this.CurrentItem == 0)
                this.CurrentItem = 0;

            if (this.CurrentItemOffset == 0)
                this.CurrentItemOffset = 1;

            if (this.TimeSpanMiliseconds == 0)
                this.TimeSpanMiliseconds = 800;


            this.SetChildTransform(RotateDirection.None);
        }

        /// <summary>
        /// Translates all left items to a direction
        /// </summary>
        /// <param name="direction">Direction to translate</param>
        /// <param name="toItem">From first item To item</param>
        void TranslateLeftItems(RotateDirection direction, int toItem)
        {
            for (int i = 0; i < ((direction == RotateDirection.Right) ? toItem - 1 : toItem); ++i)
            {
                InteractiveVisual3D child = this.MyInteractiveVisual3D.Children[i] as InteractiveVisual3D;
                if (child == null) return;

                Transform3DGroup transf = child.Transform as Transform3DGroup;
                TranslateTransform3D translate = (transf.Children[0] as TranslateTransform3D);
                RotateTransform3D rot = (transf.Children[1] as RotateTransform3D);
                DoubleAnimation myDoubleAnimation = null, myRotateDoubleAnimation = null;

                switch (direction)
                {
                    case RotateDirection.Right:
                        //Animate the translation's angle.
                        myDoubleAnimation = new DoubleAnimation();
                        myDoubleAnimation.From = translate.OffsetX;
                        myDoubleAnimation.To = translate.OffsetX + 1;
                        myDoubleAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(TimeSpanMiliseconds));
                        myDoubleAnimation.AutoReverse = false;
                        myDoubleAnimation.RepeatBehavior = new RepeatBehavior(1d);

                        //Apply the animation to the translation's angle property.
                        translate.BeginAnimation(TranslateTransform3D.OffsetXProperty, myDoubleAnimation);

                        // Animate the rotation's center.
                        myRotateDoubleAnimation = new DoubleAnimation();
                        myRotateDoubleAnimation.From = rot.CenterX;
                        myRotateDoubleAnimation.To = rot.CenterX + 1;
                        myRotateDoubleAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(TimeSpanMiliseconds));
                        myRotateDoubleAnimation.AutoReverse = false;
                        myRotateDoubleAnimation.RepeatBehavior = new RepeatBehavior(1d);

                        // Apply the animation to the rotation's centerx property.
                        rot.BeginAnimation(RotateTransform3D.CenterXProperty, myRotateDoubleAnimation);
                        break;
                    case RotateDirection.Left:
                        //Animate the translation's angle.
                        myDoubleAnimation = new DoubleAnimation();
                        myDoubleAnimation.From = translate.OffsetX;
                        myDoubleAnimation.To = translate.OffsetX - 1;
                        myDoubleAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(TimeSpanMiliseconds));
                        myDoubleAnimation.AutoReverse = false;
                        myDoubleAnimation.RepeatBehavior = new RepeatBehavior(1d);

                        // Apply the animation to the translation's angle property.
                        translate.BeginAnimation(TranslateTransform3D.OffsetXProperty, myDoubleAnimation);

                        // Animate the rotation's center.
                        myRotateDoubleAnimation = new DoubleAnimation();
                        myRotateDoubleAnimation.From = rot.CenterX;
                        myRotateDoubleAnimation.To = rot.CenterX - 1;
                        myRotateDoubleAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(TimeSpanMiliseconds));
                        myRotateDoubleAnimation.AutoReverse = false;
                        myRotateDoubleAnimation.RepeatBehavior = new RepeatBehavior(1d);

                        // Apply the animation to the rotation's centerx property.
                        rot.BeginAnimation(RotateTransform3D.CenterXProperty, myRotateDoubleAnimation);
                        break;
                    default:
                        //do nothing
                        break;
                }
            }
        }

        /// <summary>
        /// Translates all right items from an item to a direction
        /// </summary>
        /// <param name="direction">Direction to translate</param>
        /// <param name="fromItem">From item to translate, to last item</param>
        void TranslateRightItems(RotateDirection direction, int fromItem)
        {
            for (int i = ((direction == RotateDirection.Right) ? fromItem + 2 : fromItem + 1); i < this.MyInteractiveVisual3D.Children.Count; ++i)
            {
                InteractiveVisual3D child = this.MyInteractiveVisual3D.Children[i] as InteractiveVisual3D;
                if (child == null) return;

                Transform3DGroup transf = child.Transform as Transform3DGroup;
                TranslateTransform3D translate = (transf.Children[0] as TranslateTransform3D);
                RotateTransform3D rot = (transf.Children[1] as RotateTransform3D);
                DoubleAnimation myDoubleAnimation = null, myRotateDoubleAnimation = null;

                switch (direction)
                {
                    case RotateDirection.Right:
                        // Animate the translation's offset.
                        myDoubleAnimation = new DoubleAnimation();
                        myDoubleAnimation.From = translate.OffsetX;
                        myDoubleAnimation.To = translate.OffsetX + 1;
                        myDoubleAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(TimeSpanMiliseconds));
                        myDoubleAnimation.AutoReverse = false;
                        myDoubleAnimation.RepeatBehavior = new RepeatBehavior(1d);

                        // Apply the animation to the translation's offsetx property.
                        translate.BeginAnimation(TranslateTransform3D.OffsetXProperty, myDoubleAnimation);

                        // Animate the rotation's center.
                        myRotateDoubleAnimation = new DoubleAnimation();
                        myRotateDoubleAnimation.From = rot.CenterX;
                        myRotateDoubleAnimation.To = rot.CenterX + 1;
                        myRotateDoubleAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(TimeSpanMiliseconds));
                        myRotateDoubleAnimation.AutoReverse = false;
                        myRotateDoubleAnimation.RepeatBehavior = new RepeatBehavior(1d);

                        // Apply the animation to the rotation's centerx property.
                        rot.BeginAnimation(RotateTransform3D.CenterXProperty, myRotateDoubleAnimation);
                        break;
                    case RotateDirection.Left:
                        // Animate the translation's offset.
                        myDoubleAnimation = new DoubleAnimation();
                        myDoubleAnimation.From = translate.OffsetX;
                        myDoubleAnimation.To = translate.OffsetX - 1;
                        myDoubleAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(TimeSpanMiliseconds));
                        myDoubleAnimation.AutoReverse = false;
                        myDoubleAnimation.RepeatBehavior = new RepeatBehavior(1d);
                        // Apply the animation to the translation's angle property.
                        translate.BeginAnimation(TranslateTransform3D.OffsetXProperty, myDoubleAnimation);

                        // Animate the rotation's center.
                        myRotateDoubleAnimation = new DoubleAnimation();
                        myRotateDoubleAnimation.From = rot.CenterX;
                        myRotateDoubleAnimation.To = rot.CenterX - 1;
                        myRotateDoubleAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(TimeSpanMiliseconds));
                        myRotateDoubleAnimation.AutoReverse = false;
                        myRotateDoubleAnimation.RepeatBehavior = new RepeatBehavior(1d);

                        // Apply the animation to the rotation's centerx property.
                        rot.BeginAnimation(RotateTransform3D.CenterXProperty, myRotateDoubleAnimation);
                        break;
                    default:
                        //do nothing
                        break;
                }
            }
        }

        /// <summary>
        /// Rotates the stack on a direction
        /// </summary>
        /// <param name="direction">Rotate direction</param>
        void RotateStack(RotateDirection direction)
        {
            //reset the current item transformations
            this.ResetCurrentItem(direction);

            #region translate the left items with one unit
            this.TranslateLeftItems(direction, this.CurrentItem);
            #endregion

            //bring to front next current item and put the last one in its place
            int index = this.CurrentItem;
            this.RotateItems(direction, ref index);
            this.CurrentItem = index;

            #region translate the right items with one unit
            this.TranslateRightItems(direction, this.CurrentItem);
            #endregion
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="direction"></param>
        /// <param name="index"></param>
        private void RotateItems(RotateDirection direction, ref int index)
        {
            InteractiveVisual3D currentChild = this.MyInteractiveVisual3D.Children[index] as InteractiveVisual3D;
            if (currentChild == null) return;

            Transform3DGroup currentTransf = currentChild.Transform as Transform3DGroup;
            TranslateTransform3D currentTranslate = (currentTransf.Children[0] as TranslateTransform3D);
            RotateTransform3D currentRotation = (currentTransf.Children[1] as RotateTransform3D);

            DoubleAnimation myCurrentTranslateXDoubleAnimation = null,
                            myCurrentTranslateZDoubleAnimation = null,
                            myCurrentRotateDoubleAnimation = null;

            switch (direction)
            {
                case RotateDirection.Right:
                    #region itemIndex

                    // Animate the rotation's angle.
                    currentRotation.CenterX = 1; currentRotation.CenterZ = 0;
                    myCurrentRotateDoubleAnimation = new DoubleAnimation();
                    myCurrentRotateDoubleAnimation.From = 0;
                    myCurrentRotateDoubleAnimation.To = -45;
                    myCurrentRotateDoubleAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(TimeSpanMiliseconds / 2));
                    myCurrentRotateDoubleAnimation.AutoReverse = false;
                    myCurrentRotateDoubleAnimation.RepeatBehavior = new RepeatBehavior(1d);

                    // Apply the animation to the rotation's angle property.
                    currentRotation.Rotation.BeginAnimation(AxisAngleRotation3D.AngleProperty, myCurrentRotateDoubleAnimation);

                    // Animate the Translate's OffsetX.
                    myCurrentTranslateXDoubleAnimation = new DoubleAnimation();
                    myCurrentTranslateXDoubleAnimation.From = 0;
                    myCurrentTranslateXDoubleAnimation.To = 1;
                    myCurrentTranslateXDoubleAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(TimeSpanMiliseconds / 2));
                    myCurrentTranslateXDoubleAnimation.AutoReverse = false;
                    myCurrentTranslateXDoubleAnimation.RepeatBehavior = new RepeatBehavior(1d);

                    // Apply the animation to the Translate's OffsetX property.
                    currentTranslate.BeginAnimation(TranslateTransform3D.OffsetXProperty, myCurrentTranslateXDoubleAnimation);

                    // Animate the Translate's OffsetZ.
                    myCurrentTranslateZDoubleAnimation = new DoubleAnimation();
                    myCurrentTranslateZDoubleAnimation.From = this.CurrentItemOffset;
                    myCurrentTranslateZDoubleAnimation.To = 0;
                    myCurrentTranslateZDoubleAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(TimeSpanMiliseconds / 2));
                    myCurrentTranslateZDoubleAnimation.AutoReverse = false;
                    myCurrentTranslateZDoubleAnimation.RepeatBehavior = new RepeatBehavior(1d);

                    // Apply the animation to the Translate's OffsetX property.
                    currentTranslate.BeginAnimation(TranslateTransform3D.OffsetZProperty, myCurrentTranslateZDoubleAnimation);

                    #endregion
                    --index;
                    #region itemIndex - 1

                    currentChild = this.MyInteractiveVisual3D.Children[index] as InteractiveVisual3D;
                    if (currentChild == null) return;

                    currentTransf = currentChild.Transform as Transform3DGroup;
                    currentTranslate = (currentTransf.Children[0] as TranslateTransform3D);
                    currentRotation = (currentTransf.Children[1] as RotateTransform3D);

                    // Animate the rotation's angle.
                    currentRotation.CenterX = -1; currentRotation.CenterZ = 0;
                    myCurrentRotateDoubleAnimation = new DoubleAnimation();
                    myCurrentRotateDoubleAnimation.From = 45;
                    myCurrentRotateDoubleAnimation.To = 0;
                    myCurrentRotateDoubleAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(TimeSpanMiliseconds / 2));
                    myCurrentRotateDoubleAnimation.AutoReverse = false;
                    myCurrentRotateDoubleAnimation.RepeatBehavior = new RepeatBehavior(1d);

                    // Apply the animation to the rotation's angle property.
                    currentRotation.Rotation.BeginAnimation(AxisAngleRotation3D.AngleProperty, myCurrentRotateDoubleAnimation);

                    // Animate the Translate's OffsetX.
                    myCurrentTranslateXDoubleAnimation = new DoubleAnimation();
                    myCurrentTranslateXDoubleAnimation.From = -1;
                    myCurrentTranslateXDoubleAnimation.To = 0;
                    myCurrentTranslateXDoubleAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(TimeSpanMiliseconds / 2));
                    myCurrentTranslateXDoubleAnimation.AutoReverse = false;
                    myCurrentTranslateXDoubleAnimation.RepeatBehavior = new RepeatBehavior(1d);

                    // Apply the animation to the Translate's OffsetX property.
                    currentTranslate.BeginAnimation(TranslateTransform3D.OffsetXProperty, myCurrentTranslateXDoubleAnimation);

                    // Animate the Translate's OffsetZ.
                    myCurrentTranslateZDoubleAnimation = new DoubleAnimation();
                    myCurrentTranslateZDoubleAnimation.From = 0;
                    myCurrentTranslateZDoubleAnimation.To = this.CurrentItemOffset;
                    myCurrentTranslateZDoubleAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(TimeSpanMiliseconds / 2));
                    myCurrentTranslateZDoubleAnimation.AutoReverse = false;
                    myCurrentTranslateZDoubleAnimation.RepeatBehavior = new RepeatBehavior(1d);

                    // Apply the animation to the Translate's OffsetX property.
                    currentTranslate.BeginAnimation(TranslateTransform3D.OffsetZProperty, myCurrentTranslateZDoubleAnimation);
                    #endregion
                    break;
                case RotateDirection.Left:
                    #region itemIndex

                    // Animate the rotation's angle.
                    currentRotation.CenterX = -1; currentRotation.CenterZ = 0;
                    myCurrentRotateDoubleAnimation = new DoubleAnimation();
                    myCurrentRotateDoubleAnimation.From = 0;
                    myCurrentRotateDoubleAnimation.To = 45;
                    myCurrentRotateDoubleAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(TimeSpanMiliseconds / 2));
                    myCurrentRotateDoubleAnimation.AutoReverse = false;
                    myCurrentRotateDoubleAnimation.RepeatBehavior = new RepeatBehavior(1d);
                    // Apply the animation to the rotation's angle property.
                    currentRotation.Rotation.BeginAnimation(AxisAngleRotation3D.AngleProperty, myCurrentRotateDoubleAnimation);

                    //currentTranslate = new TranslateTransform3D(-constVal, 0, this.CurrentItemOffset - constVal);
                    // Animate the Translate's OffsetX.
                    myCurrentTranslateXDoubleAnimation = new DoubleAnimation();
                    myCurrentTranslateXDoubleAnimation.From = 0;
                    myCurrentTranslateXDoubleAnimation.To = -1;
                    myCurrentTranslateXDoubleAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(TimeSpanMiliseconds / 2));
                    myCurrentTranslateXDoubleAnimation.AutoReverse = false;
                    myCurrentTranslateXDoubleAnimation.RepeatBehavior = new RepeatBehavior(1d);
                    // Apply the animation to the Translate's OffsetX property.
                    currentTranslate.BeginAnimation(TranslateTransform3D.OffsetXProperty, myCurrentTranslateXDoubleAnimation);

                    //Animate the Translate's OffsetZ.
                    myCurrentTranslateZDoubleAnimation = new DoubleAnimation();
                    myCurrentTranslateZDoubleAnimation.From = this.CurrentItemOffset;
                    myCurrentTranslateZDoubleAnimation.To = 0;
                    myCurrentTranslateZDoubleAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(TimeSpanMiliseconds / 2));
                    myCurrentTranslateZDoubleAnimation.AutoReverse = false;
                    myCurrentTranslateZDoubleAnimation.RepeatBehavior = new RepeatBehavior(1d);

                    //Apply the animation to the Translate's OffsetX property.
                    currentTranslate.BeginAnimation(TranslateTransform3D.OffsetZProperty, myCurrentTranslateZDoubleAnimation);
                    #endregion
                    ++index;
                    #region itemIndex + 1

                    currentChild = this.MyInteractiveVisual3D.Children[index] as InteractiveVisual3D;
                    if (currentChild == null) return;

                    Transform3DGroup currentTransf1 = currentChild.Transform as Transform3DGroup;
                    TranslateTransform3D currentTranslate1 = (currentTransf1.Children[0] as TranslateTransform3D);
                    RotateTransform3D currentRotation1 = (currentTransf1.Children[1] as RotateTransform3D);

                    // Animate the rotation's angle.
                    currentRotation1.CenterX = 1; currentRotation1.CenterZ = 0;
                    myCurrentRotateDoubleAnimation = new DoubleAnimation();
                    myCurrentRotateDoubleAnimation.From = -45;
                    myCurrentRotateDoubleAnimation.To = 0;
                    myCurrentRotateDoubleAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(TimeSpanMiliseconds / 2));
                    myCurrentRotateDoubleAnimation.AutoReverse = false;
                    myCurrentRotateDoubleAnimation.RepeatBehavior = new RepeatBehavior(1d);
                    // Apply the animation to the rotation's angle property.
                    currentRotation1.Rotation.BeginAnimation(AxisAngleRotation3D.AngleProperty, myCurrentRotateDoubleAnimation);

                    //currentTranslate = new TranslateTransform3D(-constVal, 0, this.CurrentItemOffset - constVal);
                    // Animate the Translate's OffsetX.
                    myCurrentTranslateXDoubleAnimation = new DoubleAnimation();
                    myCurrentTranslateXDoubleAnimation.From = 1;
                    myCurrentTranslateXDoubleAnimation.To = 0;
                    myCurrentTranslateXDoubleAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(TimeSpanMiliseconds / 2));
                    myCurrentTranslateXDoubleAnimation.AutoReverse = false;
                    myCurrentTranslateXDoubleAnimation.RepeatBehavior = new RepeatBehavior(1d);
                    // Apply the animation to the Translate's OffsetX property.
                    currentTranslate1.BeginAnimation(TranslateTransform3D.OffsetXProperty, myCurrentTranslateXDoubleAnimation);

                    //Animate the Translate's OffsetZ.
                    myCurrentTranslateZDoubleAnimation = new DoubleAnimation();
                    myCurrentTranslateZDoubleAnimation.From = 0;
                    myCurrentTranslateZDoubleAnimation.To = this.CurrentItemOffset;
                    myCurrentTranslateZDoubleAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(TimeSpanMiliseconds / 2));
                    myCurrentTranslateZDoubleAnimation.AutoReverse = false;
                    myCurrentTranslateZDoubleAnimation.RepeatBehavior = new RepeatBehavior(1d);

                    //Apply the animation to the Translate's OffsetX property.
                    currentTranslate1.BeginAnimation(TranslateTransform3D.OffsetZProperty, myCurrentTranslateZDoubleAnimation);
                    #endregion
                    break;
                default:
                    //do nothing
                    break;
            }
        }

        /// <summary>
        /// Creates all children
        /// </summary>
        void CreateChildren()
        {
            for (int index = 0; index < this.Children.Count; ++index)
            {
                _3DViewElement child = this.Children[index];

                this.AddChild(child);
            }
        }

        /// <summary>
        /// Transforms all children in a direction
        /// </summary>
        /// <param name="direction">Rotate direction</param>
        void SetChildTransform(RotateDirection direction)
        {
            CreateChildren();

            this.ResetCurrentItem(direction);

            for (int i = 0; i < this.MyInteractiveVisual3D.Children.Count; ++i)
            {
                InteractiveVisual3D child = this.MyInteractiveVisual3D.Children[i] as InteractiveVisual3D;

                if (child == null) return;
                Transform3DGroup transf = child.Transform as Transform3DGroup;

                if (transf == null) transf = this.CreateTransform();

                FrameworkElement panel = child.Visual as FrameworkElement;
                panel.RenderTransform = __ElementRotation;

                int index = i;

                RotateTransform3D rot = (transf.Children[1] as RotateTransform3D);
                TranslateTransform3D translate = (transf.Children[0] as TranslateTransform3D);

                transf.Children.Clear();

                if (index == this.CurrentItem)
                {
                    translate.OffsetX = 0;
                    translate.OffsetZ = this.CurrentItemOffset;

                    rot.CenterZ = this.CurrentItemOffset;
                    rot.Rotation.SetValue(AxisAngleRotation3D.AngleProperty, (double)0);
                }
                else
                    if (index < this.CurrentItem)
                    {
                        if (direction == RotateDirection.None)
                        {
                            translate.OffsetX = index - this.CurrentItem;
                            rot.CenterX = index - this.CurrentItem;
                            rot.Rotation.SetValue(AxisAngleRotation3D.AngleProperty, (double)45);
                        }
                        else if (direction == RotateDirection.Left)
                        {
                            //we want to see a left item
                            translate.OffsetX++;
                            translate.OffsetZ = 0;
                            rot.CenterX = translate.OffsetX;
                            rot.CenterZ = 0;
                            rot.Rotation.SetValue(AxisAngleRotation3D.AngleProperty, (double)45);
                        }
                        else
                        {
                            //we want to see a right item
                            translate.OffsetX--;
                            translate.OffsetZ = 0;
                            rot.CenterX = translate.OffsetX;
                            rot.CenterZ = 0;
                            rot.Rotation.SetValue(AxisAngleRotation3D.AngleProperty, (double)45);
                        }
                    }
                    else
                    {
                        if (direction == RotateDirection.None)
                        {
                            translate.OffsetX = index - this.CurrentItem;
                            rot.CenterX = index - this.CurrentItem;
                            rot.Rotation.SetValue(AxisAngleRotation3D.AngleProperty, (double)-45);
                        }
                        else if (direction == RotateDirection.Left)
                        {
                            //we want to see a left item
                            translate.OffsetX++;
                            translate.OffsetZ = 0;
                            rot.CenterX = translate.OffsetX;
                            rot.CenterZ = 0;
                            rot.Rotation.SetValue(AxisAngleRotation3D.AngleProperty, (double)-45);
                        }
                        else
                        {
                            //we want to see a right item
                            translate.OffsetX--;
                            translate.OffsetZ = 0;
                            rot.CenterX = translate.OffsetX;
                            rot.CenterZ = 0;
                            rot.Rotation.SetValue(AxisAngleRotation3D.AngleProperty, (double)-45);
                        }
                    }

                transf.Children.Add(translate); transf.Children.Add(rot);
                child.Transform = transf;
            }
        }

        /// <summary>
        /// Resets the current item transform
        /// </summary>
        /// <param name="direction"></param>
        private void ResetCurrentItem(RotateDirection direction)
        {
            switch (direction)
            {
                case RotateDirection.Left:
                    //reset current item
                    InteractiveVisual3D child1 = this.MyInteractiveVisual3D.Children[this.CurrentItem] as InteractiveVisual3D;
                    if (child1 == null) return;
                    Transform3DGroup transf1 = child1.Transform as Transform3DGroup;
                    transf1.Children[0] = new TranslateTransform3D(0, 0, this.CurrentItemOffset);
                    transf1.Children[1] = new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 1, 0), 0d));
                    child1.Transform = transf1;

                    break;
                case RotateDirection.Right:
                    child1 = this.MyInteractiveVisual3D.Children[this.CurrentItem] as InteractiveVisual3D;
                    if (child1 == null) return;

                    transf1 = child1.Transform as Transform3DGroup;
                    transf1.Children[0] = new TranslateTransform3D(0, 0, this.CurrentItemOffset);
                    transf1.Children[1] = new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 1, 0), 0d));
                    child1.Transform = transf1;
                    break;
                default:
                    break;
            }
        }
        #endregion

        #region Event handlers

        /// <summary>
        /// Fires on Left rotation button click
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void LeftButton_Click(object sender, RoutedEventArgs e)
        {
            if (this.CurrentItem > 0)
                this.RotateStack(RotateDirection.Right);
        }

        /// <summary>
        /// Fires on Right rotation button click
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void RightButton_Click(object sender, RoutedEventArgs e)
        {
            if (this.CurrentItem < this.MyInteractiveVisual3D.Children.Count - 1)
                this.RotateStack(RotateDirection.Left);
        }

        /// <summary>
        /// Flips current item
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void FlipButton_Click(object sender, RoutedEventArgs e)
        {
            Transform3DGroup transf = this.MyInteractiveVisual3D.Children[this.CurrentItem].Transform as Transform3DGroup;

            RotateTransform3D rot = transf.Children[1] as RotateTransform3D;

            double angle = (double)rot.Rotation.GetValue(AxisAngleRotation3D.AngleProperty);

            rot = new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 1, 0), angle));
            rot.CenterX = 0;
            rot.CenterY = 0;
            rot.CenterZ = this.CurrentItemOffset;
            transf.Children[1] = rot;

            // Animate the rotation's angle.
            DoubleAnimation myDoubleAnimation = new DoubleAnimation();
            myDoubleAnimation.From = angle;
            myDoubleAnimation.To = 180 - Math.Abs(angle);
            myDoubleAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(500));
            myDoubleAnimation.AutoReverse = false;
            myDoubleAnimation.RepeatBehavior = new RepeatBehavior(1d);
            // Apply the animation to the rotation's angle property.
            rot.Rotation.BeginAnimation(AxisAngleRotation3D.AngleProperty, myDoubleAnimation);

            InteractiveVisual3D currentChild = this.MyInteractiveVisual3D.Children[this.CurrentItem] as InteractiveVisual3D;
            StackPanel content = currentChild.Visual as StackPanel;

            if (angle == 0)
            {
                content.Children[0].Visibility = Visibility.Collapsed;
                content.Children[1].Visibility = Visibility.Collapsed;

                content.Children[2].Visibility = Visibility.Visible;
                content.Children[3].Visibility = Visibility.Visible;
            }
            else
            {
                content.Children[2].Visibility = Visibility.Collapsed;
                content.Children[3].Visibility = Visibility.Collapsed;

                content.Children[0].Visibility = Visibility.Visible;
                content.Children[1].Visibility = Visibility.Visible;
            }
        }

        #endregion
    }

    public class _3DViewElement : FrameworkElement
    {
        /// <summary>
        /// Gets the front element
        /// </summary>
        public FrameworkElement FrontElement { get; set; }

        /// <summary>
        /// Gets the back element
        /// </summary>
        public FrameworkElement BackElement { get; set; }
    }
}
